package com.ruoyi.web.controller.utils;

import com.ruoyi.common.utils.sign.Md5Utils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.apache.logging.log4j.util.PropertiesUtil;
import org.jdom2.JDOMException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * WeixinpayService
 *
 * @author
 * @version 1.0.0
 * @since 2017年02月24日 09:36
 **/
@Service
public class XiaochengxuPayService {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private static final String URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";;
    //private static final String APP_ID = "wx38ab6dbc77769a53";
    //我们这边测试的小程序ID
    //private static final String APP_ID = "wxfa11bb7ddec0b1e0";
    //五寸方
    private static final String APP_ID = "wxb2bacd262ba9746b";
    private static final String MCH_ID = "1622749132";
    private static final String TRADE_TYPE = "JSAPI";
    private static final String SIGN_TYPE = "MD5";
    private static final String CHARSET = "UTF-8";
    private static final String REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund";

    private static String Key = "2e15db0ce34d4a2abfe7adfb43346d9a";

    //退款所需
    private  static int socketTimeout = 10000;// 连接超时时间，默认10秒
    private  static int connectTimeout = 30000;// 传输超时时间，默认30秒
    private  static RequestConfig requestConfig;// 请求器的配置
    private  static CloseableHttpClient httpClient;// HTTP请求器

    /**
     * @param args
     */
    public static void main(String[] args) {
//        System.out.println(MD5Util.getRandomStringByLength(32));
//        System.out.println(MD5Util.getRandomStringByLength(32).length());
        SortedMap<String ,String> map = new TreeMap<>();
        map.put("body", "套餐名称"); //商品描述  必
        map.put("attach", "套餐附加信息"); //商品详情  非
        map.put("openid", "12345678"); //商品详情  非
        map.put("out_trade_no", "20879403372943360"); //商户订单号  必
        map.put("total_fee", "1"); //总金额  必
        map.put("spbill_create_ip", "182.119.183.6"); //终端ip  用户端ip  必
    }

    /**
     * 微信支付签名算法sign
     * @param characterEncoding
     * @param parameters
     * @return
     */
    public static String createSign(String characterEncoding,SortedMap<String,Object> parameters){
        StringBuffer sb = new StringBuffer();
        Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序（升序）
        Iterator it = es.iterator();
        while(it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String k = (String)entry.getKey();
            Object v = entry.getValue();
            if(null != v && !"".equals(v)
                    && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + Key);
        System.out.println("拼接信息:"+sb);
        String sign = Md5Utils.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
        return sign;
    }


    /**
     * @param map
     * @return
     */
    public Map<String,Object> signature(SortedMap map) {
        logger.error("签名前信息");
        logger.error(map.toString());
        String sign =  createSign(CHARSET,map);
        logger.error("签名信息");
        logger.error(sign);
        String xml ="<xml>\n" +
                "   <appid>"+map.get("appid")+"</appid>\n" +
                "   <attach>"+map.get("attach")+"</attach>" +
                "   <body>"+map.get("body")+"</body>" +
                "   <mch_id>"+map.get("mch_id")+"</mch_id>" +
                "   <nonce_str>"+map.get("nonce_str")+"</nonce_str>" +
                "   <notify_url>"+map.get("notify_url")+"</notify_url>" +
                "   <openid>"+map.get("openid")+"</openid>" +
                "   <out_trade_no>"+map.get("out_trade_no")+"</out_trade_no>" +
                "   <spbill_create_ip>"+map.get("spbill_create_ip")+"</spbill_create_ip>" +
                "   <time_start>"+map.get("time_start")+"</time_start>" +
                "   <total_fee>"+map.get("total_fee")+"</total_fee>" +
                "   <trade_type>"+map.get("trade_type")+"</trade_type>" +
                "   <sign>"+sign+"</sign>" +
                "</xml>";
        logger.error("------XML-------");
        logger.error(xml);
        String result = PayCommonUtil.httpsRequest(
                URL, "POST", xml);
        logger.error("统一下单结果");
        logger.error(result);
        Map<String, String> resultMap = null;
        try {
            resultMap = PayCommonUtil.doXMLParse(result);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        SortedMap<String, Object> relMap = new TreeMap<String, Object>();
        relMap.put("appId", APP_ID);
        String noncestr =map.get("nonce_str").toString();
        relMap.put("nonceStr", noncestr);
        relMap.put("package", "prepay_id="+resultMap.get("prepay_id"));
        relMap.put("timeStamp", System.currentTimeMillis()/1000);
        relMap.put("signType", "MD5");
        String sign2 = createSign(CHARSET, relMap);
        relMap.put("paySign",sign2);
        return relMap ;
    }


    public Map<String,Object> getOrderSignature(SortedMap map) {
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
        map.put("nonce_str", Md5Utils.getRandomStringByLength(32));
        map.put("time_start",df.format(new Date()));
        //map.put("notify_url","https://www.maoxiaofu.com/api/pay/weixinpayNotify");
        map.put("notify_url","https://xcx.shijinjiapei.com/v4/web/xuansTea/api/pay/weixinpayNotify");
        map.put("appid", APP_ID); //应用ID  必
        map.put("mch_id", MCH_ID); //商户号  必
        //map.put("sign_type", "MD5"); //签名类型默认MD5
        map.put("trade_type", TRADE_TYPE); //交易类型  APP 必
        //signMap.put("time_expire", order.getTotalAmount()); //交易结束时间    大于5分钟
        return this.signature(map);
    }

    //微信XCX退款
    public static String wxXcxRefund(SortedMap map) {
        map.put("appid", APP_ID);
        map.put("mch_id", MCH_ID);
        map.put("sign_type", "MD5");
        // 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
        String preStr = PayCommonUtil.createLinkString(map);
        //MD5运算生成签名，这里是第一次签名，用于调用统一下单接口
        String mySign = PayCommonUtil.sign(preStr, Key, "utf-8").toUpperCase();
        map.put("sign", mySign);
        //拼接统一下单接口使用的xml数据，要将上一步生成的签名一起拼接进去  支付结果通知的xml格式数据
        String xmlStr = postData(REFUND_URL, PayCommonUtil.GetMapToXML(map));
        System.out.println(xmlStr);
        Map notifyMap = null;
        try {
            notifyMap = PayCommonUtil.doXMLParse(xmlStr);
        } catch (JDOMException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        if ("SUCCESS".equals(notifyMap.get("return_code"))) {
            if("SUCCESS".equals(notifyMap.get("result_code"))) {
                //退款成功的操作
//                OrderOrderDO orderOrderDO = new OrderOrderDO();
//                orderOrderDO.setId(orderId);
//                orderOrderDO.setStatus(6);
//                orderOrderService.backOrder(orderOrderDO);
//                return R.ok("退款成功");
                return "";
            }else{
                System.out.println("退款失败:原因"+notifyMap.get("err_code_des"));
                return "退款失败:原因--"+notifyMap.get("err_code_des");
            }
        }else{
            System.out.println("退款失败:原因"+notifyMap.get("return_msg"));
            return "退款失败:原因--"+notifyMap.get("return_msg");
        }
    }

    /**
     * 加载证书
     *
     */
    private static void initCert() throws Exception {
        // 证书密码，默认为商户ID
        String key = MCH_ID;
        // 商户证书的路径, 读取本机存放的PKCS12证书文件
        InputStream instream = PropertiesUtil.class.getResourceAsStream("/apiclient_cert.p12");
        // 指定读取证书格式为PKCS12
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        try {
            // 指定PKCS12的密码(商户ID)
            keyStore.load(instream, key.toCharArray());
        } finally {
            instream.close();
        }
        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, key.toCharArray()).build();
        // 指定TLS版本
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        // 设置httpclient的SSLSocketFactory
        httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
    }


    /**
     * 通过Https往API post xml数据
     * @param url  API地址
     * @param xmlObj   要提交的XML数据对象
     * @return
     */
    public static String postData(String url, String xmlObj) {
        // 加载证书
        try {
            initCert();
        } catch (Exception e) {
            e.printStackTrace();
        }
        String result = null;
        HttpPost httpPost = new HttpPost(url);
        // 得指明使用UTF-8编码，否则到API服务器XML的中文不能被成功识别
        StringEntity postEntity = new StringEntity(xmlObj, "UTF-8");
        httpPost.addHeader("Content-Type", "text/xml");
        httpPost.setEntity(postEntity);
        // 根据默认超时限制初始化requestConfig
        requestConfig = RequestConfig.custom()
                .setSocketTimeout(socketTimeout)
                .setConnectTimeout(connectTimeout)
                .build();
        // 设置请求器的配置
        httpPost.setConfig(requestConfig);
        try {
            HttpResponse response = null;
            try {
                response = httpClient.execute(httpPost);
            }  catch (IOException e) {
                e.printStackTrace();
            }
            HttpEntity entity = response.getEntity();
            try {
                result = EntityUtils.toString(entity, "UTF-8");
            }  catch (IOException e) {
                e.printStackTrace();
            }
        } finally {
            httpPost.abort();
        }
        return result;
    }
}
